/*
 * @Author: your name
 * @Date: 2021-04-15 11:21:29
 * @LastEditTime: 2022-08-01 11:15:53
 * @LastEditors: Zhang Rida
 * @Description: In User Settings Edit
 * @FilePath: /WEILA_SDK_MODULE/src/wlsdk/src/burst_media.c
 */
#include "burst_media.h"

#include "repo.h"
#include "weilapb_defines.h"
#include "wl_audio.h"
#include "wlsdk_define.h"
#include "wl_ext_os.h"
#include "wl_utils.h"

#ifdef WL_USING_EXT_OPUS
#include "wl_ext_opus.h"
#endif

#include "weilapb.h"

#ifdef SPEEXDSP
#    include "sonic_lite.h"
#    include "speex/speex_echo.h"
#    include "speex/speex_preprocess.h"
#    define SPEEX_ECHO_NN RECORD_PCM_FRAME_LEN
#    define SPEEX_ECHO_NN_BYTES (SPEEX_ECHO_NN * 2)
#    define SPEEX_ECHO_TAIL (SPEEX_ECHO_NN * 10)
#    define SPEEX_ECHO_DELAY (RECORD_PCM_FRAME_BYTES)
#    define SPEEX_FILTER_FRAME_BYTES (RECORD_PCM_FRAME_BYTES)
#    define SPEEX_FILTER_BUFFER_BYTES (SPEEX_ECHO_DELAY + SPEEX_FILTER_FRAME_BYTES * 2)
#endif

#ifdef __cplusplus
extern "C" {
#endif

extern unsigned char burst_tone_alarm[876];
extern unsigned char burst_tone_offline[2131];
extern unsigned char burst_tone_deny[566];

extern unsigned char burst_tone_start_listen[452];
extern unsigned char burst_tone_stop_listen[465];
extern unsigned char burst_tone_start_record[388];
extern unsigned char burst_tone_stop_record[367];

extern unsigned char burst_tone_voip_ring[3803];
extern unsigned char burst_tone_voip_incoming[4096];
extern unsigned char burst_tone_voip_hangup[3923];

// #define OPUS_CODEC_DEBUG

#define PBLINK_ADD_TAIL(List, itemdata, datalen)                                                                       \
    do {                                                                                                               \
        PBLinkItem* _pItem = (PBLinkItem*)wl_malloc(sizeof(PBLinkItem) + datalen);                                     \
        if (_pItem) {                                                                                                  \
            PBLinkItem* _pTemp = wl_null;                                                                              \
            memset(_pItem, 0, sizeof(PBLinkItem) + datalen);                                                           \
            _pItem->dataSize = datalen;                                                                                \
            memcpy(_pItem->data, itemdata, datalen);                                                                   \
            _pTemp = List->pLinks;                                                                                     \
            if (_pTemp) {                                                                                              \
                while (_pTemp->pNext) {                                                                                \
                    _pTemp = _pTemp->pNext;                                                                            \
                }                                                                                                      \
                _pTemp->pNext = _pItem;                                                                                \
            } else {                                                                                                   \
                List->pLinks = _pItem;                                                                                 \
            }                                                                                                          \
            List->listCount++;                                                                                         \
        }                                                                                                              \
    } while (0)

#define PBLINK_FREE(List)                                                                                              \
    do {                                                                                                               \
        PBLinkItem* _pItem = List->pLinks;                                                                             \
        PBLinkItem* _pTemp = wl_null;                                                                                  \
        while (_pItem) {                                                                                               \
            _pTemp = _pItem;                                                                                           \
            wl_free(_pItem);                                                                                           \
            _pItem = _pTemp->pNext;                                                                                    \
        }                                                                                                              \
        List->listCount = 0;                                                                                           \
    } while (0)

#define BURST_RECORD_DROPS 20           // 丢弃开始录音数据,防止提示音被录入单位ms
#define BURST_RECORD_TIMEOUT 60000       // 最长录音60秒
#define BURST_PLAYER_WAIT_TIMEOUT 10000  // 播放器超时等待10秒
#define BURST_TIMER_INTERVAL 100         // 定时器间隔
#define BURST_PLAYER_DELAY_STOP 200     // 播放器延后关闭时间

#define OPUS_FRAME_CACHE_LEN (OPUS_FRAME_LEN * 2)
// OPUS缓存区按数据包长度2倍分配,保证不会溢出
#define OPUS_CACHED_LEN ((OPUS_FRAME_CACHE_LEN * BURST_PTT_PACKAGE_FRAMES))

typedef enum {
    BURST_MEDIA_MSG_RECORD_DATA   = 0,
    BURST_MEDIA_MSG_RECORD_RESULT = 1,
    BURST_MEDIA_MSG_PLAY_GET_DATA = 2,
    BURST_MEDIA_MSG_PLAY_RESULT   = 3,
    BURST_MEDIA_MSG_TIMER         = 4
} burst_media_module_msgid;

typedef struct __burst_media_record_data {
    WL_AUDIO_HANDLE handle;
    wl_int16        datalen;
    wl_char         data[ZERO_CONTENT_SIZE];
} burst_media_record_data;

typedef struct __burst_media_record_result {
    WL_AUDIO_HANDLE handle;
    wl_int32        result;
} burst_media_record_result;

typedef struct __burst_media_play_get_data {
    WL_AUDIO_HANDLE handle;
} burst_media_play_get_data;

typedef struct __burst_media_play_result {
    WL_AUDIO_HANDLE handle;
    wl_int32        result;
    wl_bool         isstoped;
} burst_media_play_result;

typedef struct __burst_media_recorder {
    WL_AUDIO_HANDLE handle;
#ifdef WL_USING_EXT_OPUS
    wl_ext_opus_encoder_handle    encoder;
#endif
    burst_media_recorder_callback callback;
    burst_media_end_callback      end_callback;
    void*                         context;
    wl_int32                      lifeleft;
    wl_uint32                     package_frames;
    wl_uint64                     sessionid;
    wl_uint32                     sessiontype;
    wl_int32                      prority;
    wl_uint32                     marker;
    wl_int32                      dropleft;  // 录音丢弃数
    wl_uint32                     opuscount;
    wl_int32                      opuslen;
    wl_char                       opuscached[OPUS_CACHED_LEN];
    wl_int32                      pcmlen;
    wl_char                       pcmcached[RECORD_PCM_CACHED_LEN];
    wl_bool                       stoprecord;

#ifdef SPEEXDSP  // 回声抑制
    wl_uint32             pcmfilterlen;
    wl_char               pcmfiltercached[SPEEX_FILTER_BUFFER_BYTES];
    SpeexEchoState*       st;
    SpeexPreprocessState* den;
#endif
} burst_media_recorder;

typedef struct __burst_media_audio {
    wl_uint16 type;  // 0 tone 1 audio
    wl_uint16 framecount;
    wl_uint32 playpos;
    wl_int32  frameslen;
    wl_char   frames[ZERO_CONTENT_SIZE];
} burst_media_audio;

typedef struct __burst_media_player {
    WL_AUDIO_HANDLE handle;
#ifdef WL_USING_EXT_OPUS
    wl_ext_opus_decoder_handle decorder;
#endif
    burst_media_player_callback callback;
    burst_media_end_callback    end_callback;
    wl_bool                     full;
    wl_int32                    lifeleft;
    wl_int32                    delayleft; //延后关闭剩余时间,小于等于0直接关闭播放器,用于优化频繁启动播放器开关PA引起的啵啵声
    wl_uint32                   playcount;
    wl_uint32                   playall;
    wl_uint32                   framecount;
    wl_int32                    waitcount;
    wl_int32                    prority;
    burst_media_toneid          toneid;   // 重复播放提示音Id(只在timeout大于0时有效)
    wl_int32                    timeout;  // 超时时间
    void*                       context;
    wl_list                     audio_list;
    wl_int32                    pcmlen;
    wl_char                     pcmcached[PLAY_PCM_CACHED_LEN];
#ifdef SPEEXDSP
    wl_int32 pcmfilterlen;
    wl_char  pcmfilter[SPEEX_FILTER_BUFFER_BYTES];
    float    sonicspeed;
#endif
} burst_media_player;

typedef struct __burst_media_timer {
    WL_TIMER timer;
    void*    context;
} burst_media_timer;

typedef struct {
    wl_bool              initialized;
    burst_media_recorder recorder;
    burst_media_player   player;
    WL_TIMER             timer;
    wl_ext_mutex         mutex;
    wl_uint32            record_drop_duration;
    wl_uint32            record_end_drop_duration;
} wl_burst_media_global;

static wl_burst_media_global s_burst_media_global;

static wl_int32 burst_media_message_callback(wl_int32 msgid, void* data, wl_uint32 datalen);

static wl_int32  media_start_recorder(burst_media_recorder_callback callback,
                                      burst_media_end_callback      end_callback,
                                      wl_uint64                     sessionid,
                                      wl_uint32                     sessiontype,
                                      wl_int32                      prority,
                                      wl_uint32                     package_frames,
                                      void*                         context);
static wl_int32  media_stop_recorder(void);
static wl_bool   media_is_recording(void);
static wl_bool   media_is_voip_support(void);
static wl_int32  media_get_recorder_prority(void);
static wl_int32  media_start_player(burst_media_player_callback callback,
                                    burst_media_end_callback    end_callback,
                                    wl_int32                    prority,
                                    void*                       context);
static wl_int32  media_stop_player(void);
static wl_bool   media_is_playing(void);
static void*     media_get_player_context(void);
static wl_uint32 media_get_play_frames(void);
static wl_uint32 media_get_player_left_frames(void);
static wl_int32  media_get_player_lifeleft(void);
static wl_int32  media_set_player_frame_end(void);
static wl_int32  media_add_play_audio(wl_char* frames, wl_uint32 framelen, wl_uint32 framecount);
static wl_int32  media_play_tone(wl_uint32                toneid,
                                 burst_media_end_callback end_callback,
                                 wl_int32                 prority,
                                 wl_int32                 timeout,
                                 void*                    context);
static void      meida_put_stream_data(void);
static void      burst_media_timer_callback(WL_TIMER timer, void* context);
static void      start_media_timer(void);
static void      stop_media_timer(void);
static void      recorder_end_callback(WL_AUDIO_HANDLE handle, wl_int32 reason);
static void      player_end_callback(WL_AUDIO_HANDLE handle, wl_int32 reason);
wl_int32 media_reset_player(void);

wl_int32 burst_media_init(void) {
    wl_module module;
    if (!s_burst_media_global.initialized) {
        if (0 != wl_audio_init()) {
            WLLOGW("wl_audio_init fail");
            return -1;
        }
        if (0 != wl_ext_mutex_create(&s_burst_media_global.mutex, "media")) {
            WLLOGW("wl_mutex_create fail");
            wl_audio_end();
            return -1;
        }
        memset(&module, 0, sizeof(wl_module));
        module.callback = burst_media_message_callback;
        module.taskid   = WL_TASK_MEDIA;
        module.moduleid = WL_MODULE_MEDIA;
        if (0 != wl_core_register_module(&module)) {
            wl_audio_end();
            wl_ext_mutex_unlock(s_burst_media_global.mutex);
            wl_ext_mutex_destroy(s_burst_media_global.mutex);
            WLLOGW("register module fail");
            return -1;
        }
        s_burst_media_global.record_drop_duration     = BURST_RECORD_DROPS;
        s_burst_media_global.record_end_drop_duration = 0;
        s_burst_media_global.initialized              = wl_true;
    }
    return 0;
}

wl_int32 burst_media_end(void) {
    if (s_burst_media_global.initialized) {
        wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
        if (WL_INVALID_TIMER != s_burst_media_global.timer) {
            wl_timer_stop(s_burst_media_global.timer);
            s_burst_media_global.timer = WL_INVALID_TIMER;
        }
        media_stop_player();
        media_stop_recorder();
        wl_ext_mutex_unlock(s_burst_media_global.mutex);
        wl_ext_mutex_destroy(s_burst_media_global.mutex);
        wl_audio_end();
        s_burst_media_global.initialized = wl_false;
    }
    return 0;
}

wl_bool burst_media_is_voip_support(void) {
    return media_is_voip_support();
}

wl_int32 burst_media_set_record_drop_duration(wl_uint32 duration) {
    if (duration <= 1000) {
        s_burst_media_global.record_drop_duration = duration;
    }
    return 0;
}

wl_int32 burst_media_set_record_end_drop_duration(wl_uint32 duration) {
    if (duration <= 500) {
        s_burst_media_global.record_end_drop_duration = duration;
    }
    return 0;
}

void burst_recorder_callback(void) {
    if (s_burst_media_global.recorder.callback) {
        WLLOGT("marker %d opuslen %d opuscount %d sessionid %lld",
               s_burst_media_global.recorder.marker,
               s_burst_media_global.recorder.opuslen,
               s_burst_media_global.recorder.opuscount,
               s_burst_media_global.recorder.sessionid);
        s_burst_media_global.recorder.callback(s_burst_media_global.recorder.marker,
                                               s_burst_media_global.recorder.opuslen,
                                               s_burst_media_global.recorder.opuscached,
                                               s_burst_media_global.recorder.opuscount,
                                               s_burst_media_global.recorder.sessionid,
                                               s_burst_media_global.recorder.sessiontype,
                                               s_burst_media_global.recorder.context);
        s_burst_media_global.recorder.opuslen   = 0;
        s_burst_media_global.recorder.opuscount = 0;
        memset(s_burst_media_global.recorder.opuscached, 0, sizeof(s_burst_media_global.recorder.opuscached));
    }
}

void burst_recorder_end_callback(wl_int32 resultcode) {
    if (s_burst_media_global.recorder.end_callback) {
        WLLOGI("resultcode %d", resultcode);
        s_burst_media_global.recorder.end_callback(resultcode, wl_true, s_burst_media_global.recorder.context);
    }
}

void burst_player_callback(void) {
    if (s_burst_media_global.player.callback) {
        s_burst_media_global.player.callback(s_burst_media_global.player.playcount,
                                             s_burst_media_global.player.context);
        WLLOGT("burst_player_callback count %d", s_burst_media_global.player.playcount);
        s_burst_media_global.player.playcount = 0;
    }
}

void burst_player_end_callback(wl_int32 result, wl_bool isclosed) {
    burst_player_callback();
    if (s_burst_media_global.player.end_callback) {
        WLLOGI("result %d", result);
        s_burst_media_global.player.end_callback(result, isclosed, s_burst_media_global.player.context);
        s_burst_media_global.player.callback   = wl_null;
        s_burst_media_global.player.end_callback   = wl_null;
    }
}

wl_int32 burst_media_start_voip(wl_uint64                     sessionid,
                                wl_uint32                     sessiontype,
                                burst_media_recorder_callback recorder_callback,
                                burst_media_end_callback      recorder_end_callback,
                                wl_uint32                     recorder_package_frames,
                                burst_media_player_callback   player_callback,
                                burst_media_end_callback      player_end_callback,
                                void*                         context) {
    wl_int32 ret = 0;
    WLLOGI("burst_media_start_voip sessionid %lld type %d", sessionid, sessiontype);
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    ret = media_start_recorder(recorder_callback,
                               recorder_end_callback,
                               sessionid,
                               sessiontype,
                               BURST_MEDIA_PRORITY_VOIP,
                               recorder_package_frames,
                               context);
    if (0 != ret) {
        wl_ext_mutex_unlock(s_burst_media_global.mutex);
        return ret;
    }
    media_stop_player();
    ret = media_start_player(player_callback, player_end_callback, BURST_MEDIA_PRORITY_VOIP, context);
    if (0 != ret) {
        wl_ext_mutex_unlock(s_burst_media_global.mutex);
        return ret;
    }

#ifdef USING_SONIC
    sonicInit();
#endif

    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    return ret;
}

wl_int32 burst_media_stop_voip(void) {
    wl_int32 ret = 0;
    WLLOGI("burst_media_stop_voip");
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    ret = media_stop_recorder();
    ret = media_stop_player();
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    return ret;
}

wl_int32 burst_media_start_recorder(burst_media_recorder_callback callback,
                                    burst_media_end_callback      end_callback,
                                    wl_uint64                     sessionid,
                                    wl_uint32                     sessiontype,
                                    wl_int32                      prority,
                                    wl_uint32                     package_frames,
                                    void*                         context) {
    wl_int32 ret = 0;
    WLLOGI("media_start_recorder sessionid %lld type %d", sessionid, sessiontype);
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    if (prority < s_burst_media_global.recorder.prority) {
        wl_ext_mutex_unlock(s_burst_media_global.mutex);
        WLLOGD("prority %d small than current prority %d", prority, s_burst_media_global.recorder.prority);
        return -1;
    }
    ret = media_start_recorder(callback, end_callback, sessionid, sessiontype, prority, package_frames, context);
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    return ret;
}

void burst_media_refresh_recorder_lifetime(wl_uint32 lifetime) {
    WLLOGI("burst_media_refresh_recorder_lifetime");
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    s_burst_media_global.recorder.lifeleft = lifetime;
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
}

wl_int32 burst_media_stop_recorder(void) {
    wl_int32 ret = 0;
    WLLOGI("media_stop_recorder");
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    // VOIP需要调用burst_media_stop_voip
    if (BURST_MEDIA_PRORITY_VOIP <= s_burst_media_global.recorder.prority) {
        wl_ext_mutex_unlock(s_burst_media_global.mutex);
        WLLOGD(
            "current prority %d large than voip %d", s_burst_media_global.recorder.prority, BURST_MEDIA_PRORITY_VOIP);
        return -1;
    }
    ret = media_stop_recorder();
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    return ret;
}

wl_int32 burst_media_stop_recorder_audio(void) {
    s_burst_media_global.recorder.stoprecord = wl_true;
    return 0;
}

wl_bool burst_media_is_recording(void) {
    wl_bool isrecording = wl_false;
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    isrecording = media_is_recording();
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    WLLOGI("recording %d", isrecording);
    return isrecording;
}

wl_bool burst_media_is_record_session(wl_uint64 sessionid, wl_uint32 sessiontype) {
    wl_bool isrecordsession = wl_false;
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    if (s_burst_media_global.recorder.sessionid == sessionid
        && s_burst_media_global.recorder.sessiontype == sessiontype) {
        isrecordsession = wl_true;
    }
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    WLLOGI("recording %d", isrecordsession);
    return isrecordsession;
}

wl_int32 burst_media_get_recorder_prority(void) {
    wl_int32 prority = 0;
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    prority = media_get_recorder_prority();
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    return prority;
}

wl_int32 burst_media_start_player(burst_media_player_callback callback,
                                  burst_media_end_callback    end_callback,
                                  wl_int32                    prority,
                                  void*                       context) {
    wl_int32 ret = 0;
    WLLOGI("media_start_player context %d", context);
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    if (prority < s_burst_media_global.player.prority) {
        wl_ext_mutex_unlock(s_burst_media_global.mutex);
        WLLOGD("prority %d small than current prority %d", prority, s_burst_media_global.player.prority);
        return -1;
    }
    ret = media_start_player(callback, end_callback, prority, context);
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    return ret;
}

wl_int32 burst_media_stop_player(void) {
    wl_int32 ret = 0;
    WLLOGI("burst_media_stop_player");
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    // VOIP需要调用burst_media_stop_voip
    if (BURST_MEDIA_PRORITY_VOIP <= s_burst_media_global.player.prority) {
        wl_ext_mutex_unlock(s_burst_media_global.mutex);
        WLLOGD("current prority %d large than voip %d", s_burst_media_global.player.prority, BURST_MEDIA_PRORITY_VOIP);
        return -1;
    }
    ret = media_reset_player();
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    return ret;
}

wl_int32 burst_media_stop_player_immediately(void) {
    wl_int32 ret = 0;
    WLLOGI("burst_media_stop_player_immediately");
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    // VOIP需要调用burst_media_stop_voip
    if (BURST_MEDIA_PRORITY_VOIP <= s_burst_media_global.player.prority) {
        wl_ext_mutex_unlock(s_burst_media_global.mutex);
        WLLOGD("current prority %d large than voip %d", s_burst_media_global.player.prority, BURST_MEDIA_PRORITY_VOIP);
        return -1;
    }
    ret = media_stop_player();
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    return ret;
}

wl_bool burst_media_is_playing(void) {
    wl_bool isplaying = wl_false;
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    isplaying = media_is_playing();
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    return isplaying;
}

wl_int32 burst_media_get_player_prority(void) {
    wl_int32 prority = 0;
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    prority = s_burst_media_global.player.prority;
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    WLLOGI("prority %d", prority);
    return prority;
}

void* burst_media_get_player_context(void) {
    void* context = wl_null;
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    context = media_get_player_context();
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    return context;
}

wl_uint32 burst_media_get_play_frames(void) {
    wl_uint32 frames = 0;
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    frames = media_get_play_frames();
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    return frames;
}

wl_uint32 burst_media_get_player_left_frames(void) {
    wl_uint32 frames = 0;
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    frames = media_get_player_left_frames();
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    return frames;
}

wl_int32 burst_media_get_player_lifeleft(void) {
    wl_int32 ret = 0;
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    ret = media_get_player_lifeleft();
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    return ret;
}

wl_int32 burst_media_set_player_frame_end(void) {
    wl_int32 ret = 0;
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    ret = media_set_player_frame_end();
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    return ret;
}

wl_int32 burst_media_add_play_audio(wl_char* frames, wl_uint32 framelen, wl_uint32 framecount) {
    wl_int32 ret = 0;
    WLLOGI("burst_media_add_play_audio framelen %d framecount %d", framelen, framecount);
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    ret = media_add_play_audio(frames, framelen, framecount);
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    return ret;
}

wl_int32 burst_media_play_tone(wl_uint32                toneid,
                               burst_media_end_callback end_callback,
                               wl_int32                 prority,
                               wl_int32                 timeout,
                               void*                    context) {
    wl_int32 ret = 0;
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);
    ret = media_play_tone(toneid, end_callback, prority, timeout, context);
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    return ret;
}

static wl_int32 burst_media_message_callback(wl_int32 msgid, void* data, wl_uint32 datalen) {
    WLLOGT("msgid %d", msgid);
    wl_ext_mutex_lock(s_burst_media_global.mutex, WL_EXT_WAIT_FOREVER);

    switch (msgid) {
        case BURST_MEDIA_MSG_RECORD_DATA: {
            burst_media_record_data* recorddata = (burst_media_record_data*)data;
            burst_media_recorder*    recorder   = &s_burst_media_global.recorder;
            burst_media_player*      player     = &s_burst_media_global.player;
            if (recorddata->handle == recorder->handle && wl_false == recorder->stoprecord) {
                wl_int32 opuslen = OPUS_FRAME_CACHE_LEN;
                wl_uchar opusframe[OPUS_FRAME_CACHE_LEN];

#ifdef SPEEXDSP
                {
                    wl_char endbuf[SPEEX_ECHO_NN_BYTES];
                    if (recorder->st && recorder->den) {
                        if (recorder->pcmfilterlen + recorddata->datalen <= sizeof(recorder->pcmfiltercached)) {
                            memcpy(recorder->pcmfiltercached + recorder->pcmfilterlen,
                                   recorddata->data,
                                   recorddata->datalen);
                            recorder->pcmfilterlen += recorddata->datalen;
                        }
                        // 录音缓存fliter一半再处理
                        while (recorder->pcmfilterlen >= SPEEX_ECHO_NN_BYTES) {
                            speex_echo_cancellation(recorder->st,
                                                    (spx_int16_t*)recorder->pcmfiltercached,
                                                    (spx_int16_t*)player->pcmfilter,
                                                    (spx_int16_t*)endbuf);
                            // speex_echo_capture(recorder->st, (spx_int16_t*)recorder->pcmfiltercached,
                            // (spx_int16_t*)endbuf);
                            speex_preprocess_run(recorder->den, (spx_int16_t*)endbuf);
                            // WLLOGT("speex_echo_capture filter %d", recorder->pcmfilterlen);
                            recorder->pcmfilterlen -= SPEEX_ECHO_NN_BYTES;
                            memmove(recorder->pcmfiltercached,
                                    recorder->pcmfiltercached + SPEEX_ECHO_NN_BYTES,
                                    recorder->pcmfilterlen);
                            if (player->pcmfilterlen >= SPEEX_ECHO_DELAY) {
                                player->pcmfilterlen -= SPEEX_ECHO_NN_BYTES;
                                memmove(player->pcmfilter,
                                        player->pcmfilter + SPEEX_ECHO_NN_BYTES,
                                        sizeof(player->pcmfilter) - SPEEX_ECHO_NN_BYTES);
                            } else {
                                // WLLOGW("player pcmfilter %lu", player->pcmfilterlen);
                            }
                            if (recorder->pcmlen + sizeof(endbuf) <= sizeof(recorder->pcmcached)) {
                                memcpy(recorder->pcmcached + recorder->pcmlen, endbuf, sizeof(endbuf));
                                recorder->pcmlen += sizeof(endbuf);
                            }
                        }
                    } else {
                        if (recorder->pcmlen + recorddata->datalen <= sizeof(recorder->pcmcached)) {
                            memcpy(recorder->pcmcached + recorder->pcmlen, recorddata->data, recorddata->datalen);
                            recorder->pcmlen += recorddata->datalen;
                        }
                    }
                }
#else
                if (recorder->pcmlen + recorddata->datalen <= RECORD_PCM_CACHED_LEN) {
                    memcpy(recorder->pcmcached + recorder->pcmlen, recorddata->data, recorddata->datalen);
                    recorder->pcmlen += recorddata->datalen;
                }
#endif
                WLLOGT("pcmlen %d datalen %d", recorder->pcmlen, recorddata->datalen);
                while (RECORD_PCM_FRAME_BYTES <= recorder->pcmlen) {
                    // 丢弃录音开启时数据,防止录音提示音
                    if (0 >= recorder->dropleft) {
#ifdef WL_USING_EXT_OPUS
                        // 编码opus
                        wl_int32 ret = wl_ext_opus_encode(recorder->encoder,
                                                   (wl_uchar *)recorder->pcmcached,
                                                   RECORD_PCM_FRAME_LEN,
                                                   opusframe,
                                                   opuslen);
#else
                        wl_int32 ret = 0;
#endif
                        if (ret > 0) {
                            wl_uint16 opusframelen = ret;
                            memcpy(recorder->opuscached + recorder->opuslen, &opusframelen, sizeof(wl_uint16));
                            recorder->opuslen += sizeof(wl_uint16);
                            memcpy(recorder->opuscached + recorder->opuslen, opusframe, ret);
                            recorder->opuslen += ret;
                            recorder->opuscount++;
                            WLLOGI("recorder opuscount %d", recorder->opuscount);
                            if (recorder->opuscount == recorder->package_frames) {
                                burst_recorder_callback();
                                recorder->opuscount = 0;
                                recorder->opuslen   = 0;
                                if (0 == recorder->marker) {
                                    recorder->marker = 1;
                                }
                            }
                        } else {
                            WLLOGW("opus encode fail ret %d", ret);
                        }
                    } else {
                        recorder->dropleft--;
                    }
                    recorder->pcmlen -= RECORD_PCM_FRAME_BYTES;
                    if (recorder->pcmlen > 0) {
                        memmove(recorder->pcmcached, recorder->pcmcached + RECORD_PCM_FRAME_BYTES, recorder->pcmlen);
                        WLLOGT("pcm left data len  %d", recorder->pcmlen);
                    }
                }
            }
            break;
        }
        case BURST_MEDIA_MSG_RECORD_RESULT: {
            burst_media_record_result* recordresult = (burst_media_record_result*)data;
            if (s_burst_media_global.recorder.handle != NULL
                && (recordresult->handle == s_burst_media_global.recorder.handle)) {
                burst_recorder_end_callback(recordresult->result);
                media_stop_recorder();
            } else {
                wl_audio_stop_record(recordresult->handle);
            }
            break;
        }
        case BURST_MEDIA_MSG_PLAY_GET_DATA: {
            burst_media_play_get_data* playgetdata = (burst_media_play_get_data*)data;
            if (playgetdata->handle == s_burst_media_global.player.handle) {
                meida_put_stream_data();
            } else {
                WLLOGD("BURST_MEDIA_MSG_PLAY_GET_DATA player handle %p invalid handle %p",
                       playgetdata->handle,
                       s_burst_media_global.player.handle);
                wl_audio_stop_play(playgetdata->handle);
            }
            break;
        }
        case BURST_MEDIA_MSG_PLAY_RESULT: {
            burst_media_play_result* playresult = (burst_media_play_result*)data;
            if (s_burst_media_global.player.handle != NULL
                && playresult->handle == s_burst_media_global.player.handle) {
                burst_player_end_callback(playresult->result, wl_false);
            } else {
                wl_audio_stop_play(playresult->handle);
            }
            break;
        }
        case BURST_MEDIA_MSG_TIMER: {
            burst_media_timer* mediatimer = (burst_media_timer*)data;
            if (mediatimer->timer != s_burst_media_global.timer) {
                WLLOGD("timer %d invalid", mediatimer->timer);
                wl_timer_stop(mediatimer->timer);
            }
            WLLOGI("isplaying %d player lifeleft %d isrecording %d recorder lifeleft %d",
                   media_is_playing(),
                   s_burst_media_global.player.lifeleft,
                   media_is_recording(),
                   s_burst_media_global.recorder.lifeleft);
            // 录音中
            if (media_is_recording()) {
                // VoIP不超时
                if (BURST_MEDIA_PRORITY_VOIP > s_burst_media_global.recorder.prority) {
                    s_burst_media_global.recorder.lifeleft -= BURST_TIMER_INTERVAL;
                    if (s_burst_media_global.recorder.lifeleft < 0) {
                        burst_recorder_end_callback(WLSDK_RESULT_LOCAL_BURST_TIMEOUT);
                        media_stop_recorder();
                    }
                }
            }
            // 播放中
            if (media_is_playing()) {
                if (BURST_MEDIA_PRORITY_VOIP > s_burst_media_global.player.prority) {
                    s_burst_media_global.player.lifeleft -= BURST_TIMER_INTERVAL;
                    // 播放空包超时
                    if (s_burst_media_global.player.lifeleft <= 0) {
                        s_burst_media_global.player.lifeleft = 0;
                        media_reset_player();
                        s_burst_media_global.player.delayleft -= BURST_TIMER_INTERVAL;
                        if (s_burst_media_global.player.delayleft <= 0) {
                            media_stop_player();
                        }
                    }
                }
            }

            // 未录音未播放停止定时器
            if (!media_is_recording() && !media_is_playing()) {
                stop_media_timer();
            }
            break;
        }
        default:
            WLLOGW("msgid %d not support", msgid);
            break;
    }
    
    wl_ext_mutex_unlock(s_burst_media_global.mutex);
    return 0;
}

static void recorder_callback(WL_AUDIO_HANDLE handle, const wl_uint8* data, wl_uint32 datasize) {
    // 内存达到临界不处理语音消息
    if (WL_MEMORY_MEDIA_IS_THRESHOLD) {
        burst_media_record_result recordresult;
        memset(&recordresult, 0, sizeof(burst_media_record_result));
        recordresult.handle = handle;
        recordresult.result = WLSDK_RESULT_LOCAL_OUT_OF_MEMORY;
        WLLOGD("recorder_callback core memory threshold");
        wl_core_send_message(
            WL_MODULE_MEDIA, BURST_MEDIA_MSG_RECORD_RESULT, &recordresult, sizeof(burst_media_record_result));
        WLLOGW("core memory threshold");
    } else {
        burst_media_record_data* recorddata =
            (burst_media_record_data*)wl_malloc(sizeof(burst_media_record_data) + datasize);
        if (recorddata) {
            recorddata->handle  = handle;
            recorddata->datalen = datasize;
            memcpy(recorddata->data, data, datasize);
            WLLOGI("recorder datasize %d", datasize);
            wl_core_send_message(
                WL_MODULE_MEDIA, BURST_MEDIA_MSG_RECORD_DATA, recorddata, sizeof(burst_media_record_data) + datasize);
            wl_free(recorddata);
        } else {
            burst_media_record_result recordresult;
            WLLOGD("recorder_callback malloc fail");
            memset(&recordresult, 0, sizeof(burst_media_record_result));
            recordresult.handle = handle;
            recordresult.result = WLSDK_RESULT_LOCAL_OUT_OF_MEMORY;
            wl_core_send_message(
                WL_MODULE_MEDIA, BURST_MEDIA_MSG_RECORD_RESULT, &recordresult, sizeof(burst_media_record_result));
        }
    }
}


static void recorder_end_callback(WL_AUDIO_HANDLE handle, wl_int32 reason) {
    burst_media_record_result recorderresult;
    memset(&recorderresult, 0, sizeof(burst_media_record_result));
    recorderresult.handle = handle;
    if (0 == reason) {
        recorderresult.result = WLSDK_RESULT_SUCCESS;
    } else {
        recorderresult.result = WLSDK_RESULT_LOCAL_RECORDER_ERROR;
    }

    WLLOGD("recorder_end_callback recorder end reason %d", reason);
    wl_core_send_message(
        WL_MODULE_MEDIA, BURST_MEDIA_MSG_RECORD_RESULT, &recorderresult, sizeof(burst_media_record_result));
    WLLOGI("recprder end reason %d", reason);
}

static void player_callback(WL_AUDIO_HANDLE handle) {
    burst_media_play_get_data playgetdata;
    memset(&playgetdata, 0, sizeof(burst_media_play_get_data));
    playgetdata.handle = handle;
    wl_core_send_message(
        WL_MODULE_MEDIA, BURST_MEDIA_MSG_PLAY_GET_DATA, &playgetdata, sizeof(burst_media_play_get_data));
    WLLOGT("player get data");
}

static void player_end_callback(WL_AUDIO_HANDLE handle, wl_int32 reason) {
    burst_media_play_result playresult;
    memset(&playresult, 0, sizeof(burst_media_play_result));
    playresult.handle = handle;
    if (0 == reason) {
        playresult.result = WLSDK_RESULT_SUCCESS;
    } else {
        playresult.result = WLSDK_RESULT_LOCAL_PLAYER_ERROR;
    }
    wl_core_send_message(WL_MODULE_MEDIA, BURST_MEDIA_MSG_PLAY_RESULT, &playresult, sizeof(burst_media_play_result));
    WLLOGD("player end reason %d", reason);
}

static void burst_media_timer_callback(WL_TIMER timer, void* context) {
    burst_media_timer mediatimer;
    memset(&mediatimer, 0, sizeof(burst_media_timer));
    mediatimer.timer   = timer;
    mediatimer.context = context;
    wl_core_send_message(WL_MODULE_MEDIA, BURST_MEDIA_MSG_TIMER, &mediatimer, sizeof(burst_media_timer));
}

wl_int32 media_start_recorder(burst_media_recorder_callback callback,
                              burst_media_end_callback      end_callback,
                              wl_uint64                     sessionid,
                              wl_uint32                     sessiontype,
                              wl_int32                      prority,
                              wl_uint32                     package_frames,
                              void*                         context) {
    burst_media_recorder* recorder = &s_burst_media_global.recorder;
    // 停止录音器
    media_stop_recorder();

    // 内存达到临界不处理录音消息
    if (WL_MEMORY_MEDIA_IS_THRESHOLD) {
        WLLOGW("core memory threshold");
        return -1;
    }
    memset(recorder, 0, sizeof(burst_media_recorder));
#ifdef WL_USING_EXT_OPUS
    recorder->encoder = wl_ext_opus_create_encoder(RECORD_PCM_RATE);
    if (!recorder->encoder) {
        WLLOGW("opus encoder create fail");
        return -1;
    }
#endif
    recorder->handle = wl_audio_start_record(recorder_callback, recorder_end_callback);
    if (!recorder->handle) {
#ifdef WL_USING_EXT_OPUS
        wl_ext_opus_destroy_encoder(recorder->encoder);
        recorder->encoder = wl_null;
#endif
        WLLOGW("wl_audio_start_record fail");
        return -1;
    }
    recorder->callback       = callback;
    recorder->end_callback   = end_callback;
    recorder->context        = context;
    recorder->package_frames = package_frames;
    recorder->sessionid      = sessionid;
    recorder->sessiontype    = sessiontype;
    recorder->prority        = prority;
    recorder->dropleft       = s_burst_media_global.record_drop_duration / 20;
    recorder->marker         = 0;
    recorder->lifeleft       = BURST_RECORD_TIMEOUT;
    recorder->stoprecord     = wl_false;

#ifdef SPEEXDSP
    if (BURST_MEDIA_PRORITY_VOIP == recorder->prority) {
        wl_int32 samplerate    = RECORD_PCM_RATE;
        recorder->pcmfilterlen = 0;
        recorder->st           = speex_echo_state_init(SPEEX_ECHO_NN, SPEEX_ECHO_TAIL);
        recorder->den          = speex_preprocess_state_init(SPEEX_ECHO_NN, RECORD_PCM_RATE);
        speex_echo_ctl(recorder->st, SPEEX_ECHO_SET_SAMPLING_RATE, &samplerate);
        speex_preprocess_ctl(recorder->den, SPEEX_PREPROCESS_SET_ECHO_STATE, recorder->st);
    }
#endif

    start_media_timer();

    WLLOGI("start recorder sessionid %lld", sessionid);
    return 0;
}

wl_int32 media_stop_recorder(void) {
    burst_media_recorder* recorder = &s_burst_media_global.recorder;
    // 录音句柄有效
    if (recorder->handle) {
        WL_AUDIO_HANDLE handle = recorder->handle;
        recorder->handle       = wl_null;
        recorder->marker       = recorder->marker ? 2 : 3;
        if (s_burst_media_global.record_end_drop_duration) {
            wl_uint32 drop_frames = s_burst_media_global.record_end_drop_duration / 20;
            if (recorder->opuscount > drop_frames) {
                recorder->opuscount -= drop_frames;
            } else {
                recorder->opuscount = 0;
            }
        }
        // 响应结束帧
        burst_recorder_callback();
        wl_audio_stop_record(handle);
    }
#ifdef WL_USING_EXT_OPUS
    if (recorder->encoder) {
        wl_ext_opus_destroy_encoder(recorder->encoder);
        recorder->encoder = wl_null;
    }
#endif
#ifdef SPEEXDSP
    recorder->pcmfilterlen = 0;
    if (recorder->st) {
        speex_echo_state_destroy(recorder->st);
        recorder->st = wl_null;
    }
    if (recorder->den) {
        speex_preprocess_state_destroy(recorder->den);
        recorder->den = wl_null;
    }
#endif
    memset(recorder, 0, sizeof(burst_media_recorder));
    WLLOGI("stop recorder");
    return 0;
}

wl_bool media_is_recording(void) {
    if (s_burst_media_global.recorder.handle) {
        return wl_true;
    }
    return wl_false;
}

wl_bool media_is_voip_support(void) {
    return 0 == AUDIO_VOIP_SUPPORT ? wl_false : wl_true;
}

wl_int32 media_get_recorder_prority(void) {
    return s_burst_media_global.recorder.prority;
}

wl_int32 media_init_player(wl_int32 prority) {
    burst_media_player* player = &s_burst_media_global.player;
    WLLOGD("prority %d", prority);
    // 录音中
    if (media_is_recording()) {
        // 不支持voip
        if (!media_is_voip_support()) {
            WLLOGD("media_is_recording");
            return -1;
        }
        // 支持voip比较播放与录音优先级
        else {
            if (prority < media_get_recorder_prority()) {
                WLLOGW("media player prority %d recording prority %d ", prority, media_get_recorder_prority());
                return -1;
            }
        }
    }

#ifdef WL_USING_EXT_OPUS
    if (!player->decorder) {
        player->decorder = wl_ext_opus_create_decoder(PLAY_PCM_RATE);
        if (!player->decorder) {
            WLLOGW("opus decorder malloc fail");
            return -1;
        }
    }
#endif

    if (!player->handle) {
        WLLOGD("start player stream");
#ifdef SPEEXDSP
        player->pcmfilterlen = 0;
        memset(player->pcmfilter, 0, sizeof(player->pcmfilter));
        player->sonicspeed = 1.0;
#endif
        player->playcount    = 0;
        player->playall      = 0;
        player->framecount   = 0;
        player->callback     = wl_null;
        player->end_callback = wl_null;
        player->full         = wl_false;
        player->lifeleft     = BURST_PLAYER_WAIT_TIMEOUT;
        player->context      = wl_null;
        player->prority      = prority;
        player->timeout      = 0;
        wl_list_init(&player->audio_list);
        player->handle = wl_audio_start_play(player_callback, player_end_callback);
        if (!player->handle) {
#ifdef WL_USING_EXT_OPUS
            wl_ext_opus_destroy_decoder(player->decorder);
            player->decorder = wl_null;
#endif
            WLLOGW("audio start play fail");
            return -1;
        }
        start_media_timer();
    }
    return 0;
}

wl_int32 media_start_player(burst_media_player_callback callback,
                            burst_media_end_callback    end_callback,
                            wl_int32                    prority,
                            void*                       context) {
    burst_media_player* player = &s_burst_media_global.player;

    WLLOGD("prority %d", prority);
    if (0 != media_init_player(prority)) {
        WLLOGW("media_init_player fail");
        return -1;
    }
    player->callback     = callback;
    player->end_callback = end_callback;
    player->full         = wl_false;
    player->lifeleft     = BURST_PLAYER_WAIT_TIMEOUT;
    player->delayleft    = BURST_PLAYER_DELAY_STOP;
    player->waitcount    = PLAYER_FINISH_WAIT_FRAMES;
    player->prority      = prority;
    player->context      = context;
    player->pcmlen       = 0;

#ifdef SPEEXDSP
    player->pcmfilterlen = 0;
    player->sonicspeed   = 0;
#endif

    WLLOGD("start player context %d", context);
    return 0;
}

wl_int32 media_stop_player(void) {
    burst_media_player* player = &s_burst_media_global.player;
#ifdef WL_USING_EXT_OPUS
    if (player->decorder) {
        wl_ext_opus_destroy_decoder(player->decorder);
        player->decorder = wl_null;
    }
#endif
    WLLOGI("stop player context %d prority %d handle %p", player->context, player->prority, player->handle);
    // wl_core_send_message(WL_MODULE_MEDIA, BURST_MEDIA_MSG_STOP_PLAY, wl_null, 0);
    if (player->handle) {
        wl_audio_stop_play(player->handle);
        WLLOGD("stop player stream");
        player->handle = wl_null;
        burst_player_end_callback(WLSDK_RESULT_SUCCESS, wl_true);
    }
    player->playcount  = 0;
    player->playall    = 0;
    player->framecount = 0;
    player->lifeleft   = 0;
    player->prority    = 0;
    player->callback   = wl_null;
    player->end_callback   = wl_null;
    player->context    = wl_null;
    wl_list_free(&player->audio_list);

    return 0;
}


wl_int32 media_reset_player(void) {
    burst_media_player* player = &s_burst_media_global.player;
    WLLOGI("reset player context %d prority %d handle %p", player->context, player->prority, player->handle);
    if (player->handle) {
        WLLOGD("reset player stream");
        burst_player_end_callback(WLSDK_RESULT_SUCCESS, wl_true);
    }
    player->playcount  = 0;
    player->playall    = 0;
    player->framecount = 0;
    player->lifeleft   = 0;
    player->prority    = 0;
    player->callback   = wl_null;
    player->end_callback   = wl_null;
    player->context    = wl_null;
    player->pcmlen     = 0;
    wl_list_free(&player->audio_list);

    return 0;
}

wl_bool media_is_playing(void) {
    if (s_burst_media_global.player.handle) {
        return wl_true;
    }
    return wl_false;
}

void* media_get_player_context(void) {
    return s_burst_media_global.player.context;
}

wl_uint32 media_get_play_frames(void) {
    return s_burst_media_global.player.playall;
}

wl_uint32 media_get_player_left_frames(void) {
    if (s_burst_media_global.player.framecount > s_burst_media_global.player.playall) {
        return s_burst_media_global.player.framecount - s_burst_media_global.player.playall;
    } else {
        return 0;
    }
}

wl_int32 media_get_player_lifeleft(void) {
    return s_burst_media_global.player.lifeleft;
}

wl_int32 media_set_player_frame_end(void) {
    s_burst_media_global.player.full = wl_true;
    s_burst_media_global.player.lifeleft =
        (s_burst_media_global.player.framecount - s_burst_media_global.player.playall) * BURST_FRAME_TIME
        + BURST_PLAYER_WAIT_TIMEOUT;
    s_burst_media_global.player.waitcount = PLAYER_FINISH_WAIT_FRAMES;  // 添加帧数用于解决尾音丢字问题

    WLLOGI("framecount %d playcount %d lifeleft %d",
           s_burst_media_global.player.framecount,
           s_burst_media_global.player.playall,
           s_burst_media_global.player.lifeleft);
    return 0;
}

wl_int32 media_add_play_audio(wl_char* frames, wl_uint32 framelen, wl_uint32 framecount) {
    burst_media_player* player = &s_burst_media_global.player;
    // 内存达到临界不处理语音消息
    if (WL_MEMORY_MEDIA_IS_THRESHOLD) {
        WLLOGW("core memory threshold");
        s_burst_media_global.player.playcount += framecount;
        burst_player_end_callback(WLSDK_RESULT_DATA_INVALID, wl_false);
        return -1;
    }
#ifdef WL_USING_EXT_OPUS
    if (player->decorder) {
        burst_media_audio* audio = (burst_media_audio*)wl_malloc(sizeof(burst_media_audio) + framelen);
        if (audio) {
            audio->type       = 1;
            audio->frameslen  = framelen;
            audio->framecount = framecount;
            audio->playpos    = 0;
            memcpy(audio->frames, frames, framelen);

            if (-1 == wl_list_add(&player->audio_list, audio, -1)) {
                WLLOGW("list add fail");
                wl_free(audio);
                s_burst_media_global.player.playcount += framecount;
                burst_player_end_callback(WLSDK_RESULT_DATA_INVALID, wl_false);
                return -1;
            }
            player->framecount += framecount;
            player->lifeleft += BURST_FRAME_TIME * framecount;
            WLLOGI("framecount %d lifeleft %d playcount %d", player->framecount, player->lifeleft, player->playcount);
        }
        // 数据非法
        else {
            s_burst_media_global.player.playcount += framecount;
            burst_player_end_callback(WLSDK_RESULT_DATA_INVALID, wl_false);
        }
    }
#endif

    return 0;
}

wl_int32 media_play_tone(wl_uint32                toneid,
                         burst_media_end_callback end_callback,
                         wl_int32                 prority,
                         wl_int32                 timeout,
                         void*                    context) {
    wl_uint8*           content    = wl_null;
    wl_uint32           contentlen = 0;
    burst_media_audio*  audio      = wl_null;
    burst_media_player* player     = &s_burst_media_global.player;
    wl_int32            waitcount  = PLAYER_FINISH_WAIT_FRAMES;

    WLLOGT("toneid %d", toneid);
    if (0 != media_init_player(prority)) {
        WLLOGW("media_init_player fail");
        return -1;
    }
    player->timeout      = timeout;
    player->toneid       = toneid;
    player->prority      = prority;
    player->end_callback = end_callback;
    player->full         = wl_true;
    player->lifeleft     = 0;
    player->delayleft    = BURST_PLAYER_DELAY_STOP;

    switch (toneid) {
        case BURST_MEDIA_TONE_BURST_START:
            content    = burst_tone_start_record;
            contentlen = sizeof(burst_tone_start_record);
            break;
        case BURST_MEDIA_TONE_BURST_STOP:
            content    = burst_tone_stop_record;
            contentlen = sizeof(burst_tone_stop_record);
            break;
        case BURST_MEDIA_TONE_LISTEN_START:
            waitcount  = 0;
            content    = burst_tone_start_listen;
            contentlen = sizeof(burst_tone_start_listen);
            break;
        case BURST_MEDIA_TONE_LISTEN_STOP:
            content    = burst_tone_stop_listen;
            contentlen = sizeof(burst_tone_stop_listen);
            break;
        case BURST_MEDIA_TONE_OFFLINE:
            content    = burst_tone_offline;
            contentlen = sizeof(burst_tone_offline);
            break;
        case BURST_MEDIA_TONE_ALARM:
            content    = burst_tone_alarm;
            contentlen = sizeof(burst_tone_alarm);
            break;
        case BURST_MEDIA_TONE_DENY:
            content    = burst_tone_deny;
            contentlen = sizeof(burst_tone_deny);
            break;
        case BURST_MEDIA_TONE_VOIP_INCOMING:
            content    = burst_tone_voip_incoming;
            contentlen = sizeof(burst_tone_voip_incoming);
            break;
        case BURST_MEDIA_TONE_VOIP_RING:
            content    = burst_tone_voip_ring;
            contentlen = sizeof(burst_tone_voip_ring);
            break;
        case BURST_MEDIA_TONE_VOIP_HANGUP:
            content    = burst_tone_voip_hangup;
            contentlen = sizeof(burst_tone_voip_hangup);
            break;
        default:
            content    = burst_tone_alarm;
            contentlen = sizeof(burst_tone_alarm);
            break;
    }

    if (0 == strncasecmp("#!opus", (char*)(content + 4), 6) && contentlen > 10) {
        wl_uint32 frameslen  = 0;
        wl_uint32 framecount = 0;
        burst_media_count_burst_audio((wl_char*)(content + 10), contentlen - 10, &frameslen, &framecount);

        WLLOGI("tone %d frameslen %d framecount %d", toneid, frameslen, framecount);
        audio = (burst_media_audio*)wl_malloc(sizeof(burst_media_audio) + frameslen);
        if (audio) {
            audio->type       = 0;
            audio->frameslen  = frameslen;
            audio->framecount = framecount;
            audio->playpos    = 0;
            burst_media_convert_burst_audio_to_media_audio(
                (wl_char*)(content + 10), contentlen - 10, audio->frames, frameslen);
            if (-1 == wl_list_add(&player->audio_list, audio, -1)) {
                WLLOGW("list add fail");
                wl_free(audio);
                return -1;
            }
            player->framecount += framecount;
            player->lifeleft += BURST_FRAME_TIME * framecount;
        } else {
            WLLOGW("malloc fail");
        }
        media_set_player_frame_end();
        if (waitcount > 0) {
            s_burst_media_global.player.waitcount = waitcount;
        }
    }
    WLLOGI("start play tone contentlen %d", contentlen);
    return 0;
}

void meida_put_stream_data(void) {
    burst_media_player* player = &s_burst_media_global.player;
    wl_uint8            pcmdata[PLAY_PCM_FRAME_BYTES];
    wl_int32            resultcode = WLSDK_RESULT_SUCCESS;
    wl_bool             isempty    = wl_true;

    wl_list_iterator   iterator;
    burst_media_audio* audio = wl_list_get_first(&player->audio_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator) && player->pcmlen < PLAY_PCM_FRAME_BYTES) {
        while (audio->playpos < audio->frameslen && player->pcmlen < PLAY_PCM_FRAME_BYTES) {
            wl_uint16 framelen = 0;
            wl_int32  samples  = 0;
            memcpy(&framelen, audio->frames + audio->playpos, sizeof(wl_uint16));
            audio->playpos += sizeof(wl_uint16);
#ifdef WL_USING_EXT_OPUS
            if (player->decorder) {
                samples = wl_ext_opus_decode(player->decorder, (wl_uchar*)(audio->frames + audio->playpos),
                                      framelen,
                                      pcmdata,
                                      PLAY_PCM_FRAME_LEN);
            }
#endif
            // WLLOGW("samples %d", samples);
            if (0 < samples) {
#ifdef SPEEXDSP
                // voip需要检查调速
                if (BURST_MEDIA_PRORITY_VOIP == player->prority) {
                    wl_uint32 audio_count = media_get_player_left_frames();
                    // speed小于1.1表示未调速,需要检查速度
                    if (player->sonicspeed < 1.1) {
                        // 堆积超过2秒 2倍速播放
                        if (audio_count > 100) {
                            player->sonicspeed = 1.5;
                            sonicSetSpeed(player->sonicspeed);
                        }
                        // 堆积超过500ms 1.5倍速播放
                        else if (audio_count > 50) {
                            player->sonicspeed = 1.25;
                            sonicSetSpeed(player->sonicspeed);
                        }
                    } else {
                        // 缓存小于等于10帧保持正常速率
                        if (audio_count <= 10) {
                            player->sonicspeed = 1.0;
                            sonicSetSpeed(player->sonicspeed);
                        }
                    }
                }
                if (player->sonicspeed > 1.1) {
                    wl_char endbuf[PLAY_PCM_FRAME_BYTES];
                    sonicWriteShortToStream((short*)pcmdata, samples);
                    samples = sonicReadShortFromStream((short*)endbuf, sizeof(endbuf) / 2);
                    if (samples > 0) {
                        memcpy(player->pcmcached + player->pcmlen, endbuf, samples * 2);
                        player->pcmlen += samples * 2;
                    }
                } else {
                    memcpy(player->pcmcached + player->pcmlen, pcmdata, samples * 2);
                    player->pcmlen += samples * 2;
                }
#else
                memcpy(player->pcmcached + player->pcmlen, pcmdata, samples * 2);
                player->pcmlen += samples * 2;
#endif
            } else {
                // WLLOGW("opus decode error framelen %d", framelen);
                burst_player_end_callback(WLSDK_RESULT_DATA_INVALID, wl_false);
                return;
            }
            player->playcount++;
            player->playall++;
            isempty = wl_false;
            if (player->timeout > 0) {
                player->timeout -= 20;
                if (player->timeout <= 0) {
                    burst_player_end_callback(WLSDK_RESULT_SUCCESS, wl_false);
                    return;
                }
            }
            audio->playpos += framelen;
        }
        if (audio->playpos >= audio->frameslen) {
            wl_free(audio);
            audio = (burst_media_audio*)wl_list_iterator_remove(&iterator);
        }
    }
    while (player->pcmlen >= PLAY_PCM_FRAME_BYTES) {
        wl_audio_put_play_data(player->handle, (wl_uint8*)player->pcmcached, PLAY_PCM_FRAME_BYTES);
#ifdef SPEEXDSP
        if (s_burst_media_global.recorder.st) {
            if (player->pcmfilterlen + PLAY_PCM_FRAME_BYTES <= sizeof(player->pcmfilter)) {
                memcpy(player->pcmfilter + player->pcmfilterlen, player->pcmcached, PLAY_PCM_FRAME_BYTES);
                player->pcmfilterlen += PLAY_PCM_FRAME_BYTES;
            } else {
                // WLLOGW("speex_echo_playback pcmfilterlen %lu", player->pcmfilterlen);
            }
        }
#endif
        memmove(player->pcmcached, player->pcmcached + PLAY_PCM_FRAME_BYTES, player->pcmlen - PLAY_PCM_FRAME_BYTES);
        player->pcmlen -= PLAY_PCM_FRAME_BYTES;
        // WLLOGW("player pcmlen %d", player->pcmlen);
        // 每25帧更新一次播放帧数
        if (player->playcount >= 25) {
            burst_player_callback();
        }
    }
    if (WLSDK_RESULT_SUCCESS != resultcode) {
        burst_player_end_callback(resultcode, wl_false);
    }  // 语音完整且播放完,开始尾音保持
    else if (isempty && player->full) {
        // 提示音重复播放
        if (player->timeout > 0) {
            media_play_tone(player->toneid, player->end_callback, player->prority, player->timeout, player->context);
        } else {
            if (player->waitcount-- < 0) {
                s_burst_media_global.player.waitcount = PLAYER_FINISH_WAIT_FRAMES;
                burst_player_end_callback(WLSDK_RESULT_SUCCESS, wl_false);
            }
        }
    }
}

void start_media_timer(void) {
    stop_media_timer();
    s_burst_media_global.timer = wl_timer_start(
        BURST_TIMER_INTERVAL, REPETITIVE, burst_media_timer_callback, wl_null, WL_TASK_MEDIA, "burst_media");
    WLLOGI("wl_timer_start timer %d", s_burst_media_global.timer);
}

void stop_media_timer(void) {
    WLLOGI("wl_timer_stop timer %d", s_burst_media_global.timer);
    if (WL_INVALID_TIMER != s_burst_media_global.timer) {
        wl_timer_stop(s_burst_media_global.timer);
        s_burst_media_global.timer = WL_INVALID_TIMER;
    }
}

wl_int32 burst_media_count_burst_audio(wl_char*       burst_audio,
                                       wl_uint32      burst_audio_len,
                                       OUT wl_uint32* media_audio_len,
                                       OUT wl_uint32* frames_count) {
    wl_int32 pos      = 0;
    wl_int16 framelen = 0;
    for (pos = 0; pos < burst_audio_len;) {
        // 帧长为short
        if (0x80 & burst_audio[pos]) {
            memcpy(&framelen, burst_audio + pos + 1, sizeof(wl_uint16));
            framelen = wl_ntohs(framelen);                 // 帧长
            pos += (1 + sizeof(wl_uint16));             // pos偏移帧头1字节+帧长2字节
        } else {
            framelen = (wl_uint8)burst_audio[pos + 1];  // 帧长
            pos += 1 + sizeof(wl_char);                 // pos偏移帧头1字节+帧长1字节
        }
        //非法数据
        if (0 == framelen || 255 <= framelen) {
            *media_audio_len = 0;
            *frames_count = 0;
            return -1;
        }
        pos += framelen;                                // pos 偏移帧长
        *media_audio_len +=
            (1 + sizeof(wl_uint16) + framelen);  // 一帧media数据长度1字节帧头+帧长(short)+帧数据framelen
        *frames_count += 1;
    }
    return 0;
}

wl_int32 burst_media_convert_burst_audio_to_media_audio(wl_char*  burst_audio,
                                                        wl_uint32 burst_audio_len,
                                                        wl_char*  media_audio,
                                                        wl_uint32 media_audio_len) {
    wl_int32  burst_pos      = 0;
    wl_uint16 burst_framelen = 0;
    wl_uint32 media_pos      = 0;
    wl_uint16 media_framelen = 0;
    wl_char   framehead      = 0;
    for (burst_pos = 0; burst_pos < burst_audio_len && media_pos < media_audio_len;) {
        framehead = burst_audio[burst_pos];
        // 帧长为short
        if (0x80 & burst_audio[burst_pos]) {
            memcpy(&burst_framelen, burst_audio + burst_pos + 1, sizeof(wl_uint16));
            burst_framelen = wl_ntohs(burst_framelen);                 // 帧长
            burst_pos += (1 + sizeof(wl_uint16));                   // pos偏移帧头1字节+帧长2字节
        } else {
            burst_framelen = (wl_uint8)burst_audio[burst_pos + 1];  // 帧长
            burst_pos += (1 + sizeof(wl_char));                     // pos偏移帧头1字节+帧长1字节
        }
        media_framelen = (1 + burst_framelen);                      // media帧长为1字节帧头+帧长
        // 拷贝帧长
        memcpy(media_audio + media_pos, &media_framelen, sizeof(wl_uint16));
        media_pos += sizeof(wl_uint16);
        // 拷贝帧头
        media_audio[media_pos] = framehead;
        media_pos += sizeof(wl_char);
        // 拷贝帧数据
        memcpy(media_audio + media_pos, burst_audio + burst_pos, burst_framelen);
        burst_pos += burst_framelen;
        media_pos += burst_framelen;
    }
    return 0;
}

wl_int32 burst_media_count_pcm_audio(wl_uint32      pcm_audio_len,
                                     wl_uint32      pcm_audio_rate,
                                     OUT wl_uint32* media_audio_len,
                                     OUT wl_uint32* frames_count) {
    *frames_count    = pcm_audio_len / ((pcm_audio_rate / 50) * 2);
    *media_audio_len = (OPUS_FRAME_LEN + sizeof(wl_uint16)) * (*frames_count);
    return 0;
}


wl_int32 burst_media_convert_pcm_audio_to_media_audio(wl_char*   pcm_audio,
                                                      wl_uint32  pcm_audio_len,
                                                      wl_uint32  pcm_rate,
                                                      wl_char*   media_audio,
                                                      wl_uint32* media_audio_len) {
#ifdef WL_USING_EXT_OPUS
    wl_ext_opus_encoder_handle encoder = NULL;                                                        
    wl_int32 error = wl_ext_opus_init(12000);
    if (error) {
        WLLOGW("opus init fail");
        return -1;
    }

    encoder = wl_ext_opus_create_encoder(pcm_rate);
    if (encoder) {
        wl_uint32 framelen = pcm_rate / 50 * 2;
        wl_uint32 pcmpos   = 0;
        wl_uint32 opuspos  = 0;
        wl_uchar  opusframe[OPUS_FRAME_CACHE_LEN];

        while ((pcmpos + framelen <= pcm_audio_len)
               && (opuspos + OPUS_FRAME_LEN + sizeof(wl_uint16) <= *media_audio_len)) {
            // 编码opus
            wl_int32 ret =
                wl_ext_opus_encode(encoder, (wl_uchar *)(pcm_audio + pcmpos), framelen / 2, opusframe, OPUS_FRAME_CACHE_LEN);
            if (ret > 0) {
                wl_uint16 opusframelen = ret;
                memcpy(media_audio + opuspos, &opusframelen, sizeof(wl_uint16));
                opuspos += sizeof(wl_uint16);
                memcpy(media_audio + opuspos, opusframe, ret);
                opuspos += ret;
                pcmpos += framelen;
            } else {
                WLLOGW("opus encode fail ret %d", ret);
                wl_free(encoder);
                encoder = wl_null;
                return -1;
            }
        }
        
        wl_ext_opus_destroy_encoder(encoder);
        wl_ext_opus_deinit();
        encoder          = wl_null;
        *media_audio_len = opuspos;
        return 0;
    } else {
        WLLOGW("malloc fail");
        return -1;
    }
#else
    return -1;
#endif
}

#ifdef __cplusplus
}
#endif
